/*****************************************************************************
Copyright: 2012, ustc All rights reserved.
contact:k283228391@126.com
File name: main.c
Description:using opengl in SDL.
Author:Silang Quan
Version: 1.0
Date: 2012.12.01
*****************************************************************************/
#include <SDL/SDL.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <stdio.h>
#include <stdlib.h>

#define ColoredVertex(c, v) do{ glColor3fv(c); glVertex3fv(v); }while(0)  
static int angle = 0;  
GLfloat  
    PointA[] = { 0.25f, 0.25f, -0.25f },  
    PointB[] = { 0.25f, -0.25f, -0.25f },  
    PointC[] = { -0.25f, -0.25f, -0.25f },  
    PointD[] = { -0.25f, 0.25f, -0.25f },  
    PointE[] = { 0.25f, 0.25f, 0.25f },  
    PointF[] = { 0.25f, -0.25f, 0.25f },  
    PointG[] = { -0.25f, -0.25f, 0.25f },  
    PointH[] = { -0.25f, 0.25f, 0.25f };  
GLfloat  
    ColorA[] = { 1, 0, 0 },  
    ColorB[] = { 0, 1, 0 },  
    ColorC[] = { 0, 0, 1 },  
    ColorD[] = { 1, 1, 0 },  
    ColorE[] = { 1, 0, 1 },  
    ColorF[] = { 0, 1, 1 },  
    ColorG[] = { 1, 1, 1 },  
    ColorH[] = { 0, 0, 0 };

SDL_Surface *screen;

void quit( int code )
{
    SDL_Quit( );
    /* Exit program. */
    exit( code );
}

void handleKeyEvent( SDL_keysym* keysym )
{
    switch( keysym->sym )
    {
        case SDLK_ESCAPE:
            quit( 0 );
        break;
        case SDLK_SPACE:
            break;
        default:
        break;
    }
}


void resizeGL(int width,int height)
{
    if ( height == 0 )
    {
        height = 1;
    }
    //Reset View
    glViewport( 0, 0, (GLint)width, (GLint)height );
    //Choose the Matrix mode
    glMatrixMode( GL_PROJECTION );
    //reset projection
    glLoadIdentity();
    //set perspection
    gluPerspective( 45.0, (GLfloat)width/(GLfloat)height, 0.1, 100.0 );
    //choose Matrix mode
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
}


void handleEvents()
{
    // Our SDL event placeholder.
    SDL_Event event;
    //Grab all the events off the queue.
    while( SDL_PollEvent( &event ) ) {
        switch( event.type ) {
        case SDL_KEYDOWN:
            /* 有按键按下 */
            handleKeyEvent( &event.key.keysym );
        break;
        case SDL_QUIT:

            /* 程序退出请求 */
            quit( 0 );
        break;
        case SDL_VIDEORESIZE:
            /* 窗口大小改变 */
            screen = SDL_SetVideoMode(event.resize.w, event.resize.h, 
                16, SDL_OPENGL|SDL_RESIZABLE);
            if ( screen )
            {
               resizeGL(screen->w, screen->h);
            }
        break;
        }
    }
}


/*
 * 函数名称 ： initSDL
 * 函数介绍 ： 初始化SDL相关信息
 */
void initSDL(int width,int height,int bpp,int flags)
{
    // First, initialize SDL's video subsystem.
    if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
        fprintf( stderr, "Video initialization failed: %s\n",
        SDL_GetError( ) );
        quit( 1 );
    }
    atexit(SDL_Quit);
    /* 设置OpenGL在SDL中的一些属性 */
    SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
    SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
    SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
    SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
    
    /* 设置video的模式 */
    screen= SDL_SetVideoMode( width, height, bpp,flags);
    if(!screen )
    {
        fprintf( stderr, "Video mode set failed: %s\n",SDL_GetError( ) );
        quit( 1 );
    }
    resizeGL(screen->w, screen->h);
    
    //Set caption
    SDL_WM_SetCaption( "OpenGL Test", NULL );
}


void renderGL()
{
    static int list = 0;  
    /* 清楚颜色和深度缓存 */
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    // We don't want to modify the projection matrix. */
    glMatrixMode( GL_MODELVIEW );
    /* 恢复初始坐标系 */
    glLoadIdentity();
    // Move down the z-axis.
    glTranslatef( 0.0, 0.0, -3.0 );
    
    glEnable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);
    if (list == 0)  
    {  
        list = glGenLists(1);  
        glNewList(list, GL_COMPILE);  
          
        // 面1             
        glBegin(GL_POLYGON);           //准备添加面顶点
        ColoredVertex(ColorA, PointA); //添加面的顶点1
        ColoredVertex(ColorE, PointE); //添加面的顶点2
        ColoredVertex(ColorH, PointH); //添加面的顶点3
        ColoredVertex(ColorD, PointD); //添加面的顶点4
        glEnd();                       //结束面顶点的添加
          
        // 面2  
        glBegin(GL_POLYGON);  
        ColoredVertex(ColorD, PointD);  
        ColoredVertex(ColorC, PointC);  
        ColoredVertex(ColorB, PointB);  
        ColoredVertex(ColorA, PointA);  
        glEnd();  
          
        // 面3  
        glBegin(GL_POLYGON);  
        ColoredVertex(ColorA, PointA);  
        ColoredVertex(ColorB, PointB);  
        ColoredVertex(ColorF, PointF);  
        ColoredVertex(ColorE, PointE);  
        glEnd();  
          
        // 面4  
        glBegin(GL_POLYGON);  
        ColoredVertex(ColorF, PointF);  
        ColoredVertex(ColorG, PointG);  
        ColoredVertex(ColorH, PointH);  
        ColoredVertex(ColorE, PointE);  
        glEnd();  
          
        // 面5  
        glBegin(GL_POLYGON);  
        ColoredVertex(ColorF, PointF);  
        ColoredVertex(ColorB, PointB);  
        ColoredVertex(ColorC, PointC);  
        ColoredVertex(ColorG, PointG);  
        glEnd();  

        // 面6  
        glBegin(GL_POLYGON);  
        ColoredVertex(ColorC, PointC);  
        ColoredVertex(ColorD, PointD);  
        ColoredVertex(ColorH, PointH);  
        ColoredVertex(ColorG, PointG);  
        glEnd();  
        glEndList();  
    } 

    // 已经创建了显示列表，在每次绘制正四面体时将调用它  
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
    glPushMatrix();  
    glRotatef(angle++ , 1, 0.5, 0);  
    glCallList(list);  
    glPopMatrix();  

    SDL_GL_SwapBuffers( );
}


void initGL( int width, int height )
{
    float ratio = (float) width / (float) height;
    // Our shading model--Gouraud (smooth).
    glShadeModel( GL_SMOOTH );
    /* 以白色进行清除 */
    glClearColor( 0, 0, 0, 0 );
    /* 
     * 用glViewport函数来选择绘图区域，在窗口中定义一个像素矩形，最终将图像映射到这个矩形中.
     * X，Y————以像素为单位，指定了视口的左下角（在第一象限内，以（0，0）为原点的）位置。
     * width，height————表示这个视口矩形的宽度和高度，根据窗口的实时变化重绘窗口。
     */
    glViewport( 0, 0, width, height );
    //Change to the projection matrix and set our viewing volume.
    glMatrixMode( GL_PROJECTION );
    /* 恢复初始坐标系 */
    glLoadIdentity();

    gluPerspective( 60.0, ratio, 1.0, 100.0 );
}


int main( int argc, char* argv[] )
{
    (void)argc;
    (void)argv;
    
    // Dimensions of our window.
    int width = 640;
    int height = 480;
    // Color depth in bits of our window.
    int bpp = 32;
    int flags= SDL_OPENGL|SDL_RESIZABLE;
    //Set the SDL
    initSDL(width, height, bpp,flags);
    //Set the OpenGL
    initGL( width, height );
    //main loop
    while(true)
    {
        /* 处理到来的事件 */
        handleEvents( );
        /* 绘制屏幕 */
        renderGL( );
		SDL_Delay(20);
    }
    return 0;
}